﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.ShardingRule;

namespace T.FAS.Runtime.DataAccess.CDOEngine
{
    class ConditionStatementParser
    {
        public static ConditionStatement ParseOrderByClauses(List<OrderByClause> orderByClauses, CompositeNodeObject CompositeNodeObject, DataObject dataObject)
        {
            var orderbyCondition = new ConditionStatement();
            if (orderByClauses == null || orderByClauses.Count == 0)
                return orderbyCondition;

            var list = new List<string>();
            foreach (var orderByClause in orderByClauses)
            {
                var orderByString = ParseOrderByClause(orderByClause, CompositeNodeObject, dataObject);
                if (!string.IsNullOrWhiteSpace(orderByString))
                    list.Add(orderByString);
            }

            if (list.Count > 0)
            {
                orderbyCondition.ConditionString = string.Format("ORDER BY {0}", string.Join(",", list));
            }

            return orderbyCondition;
        }

        public static string ParseOrderByClause(OrderByClause orderByClause, CompositeNodeObject CompositeNodeObject, DataObject dataObject)
        {
            var fieldType = orderByClause.FieldType;
            var columns = new Dictionary<string, DataColumn>();
            var elements = new Dictionary<string, CompositeNodeObjectElement>();
            foreach (var field in orderByClause.OrderByFields)
            {
                DataColumn column = null;
                CompositeNodeObjectElement element = null;

                switch (fieldType)
                {
                    case FieldType.Column:
                        column = dataObject.Columns.FirstOrDefault(i => i.ColumnName?.ToLower() == field?.ToLower());
                        if (column == null) //说明排序指定在其他CompositeNodeObject中
                            return null;
                        element = CompositeNodeObject.Elements.FirstOrDefault(i => i.DataColumnID == column.ID);
                        break;
                    case FieldType.Element:
                        element = CompositeNodeObject.Elements.FirstOrDefault(i => i.Name == field||i.ID == field);
                        if (element == null) //说明排序指定在其他CompositeNodeObject中
                            return null;
                        column = dataObject.Columns.FirstOrDefault(i => i.ID == element.DataColumnID);
                        break;
                    case FieldType.FunctionExpression:
                        throw new NotSupportedException("OrderByClause.FieldType.FunctionExpression");
                }

                columns.Add(field, column);
                elements.Add(field, element);
            }

            return string.Format("{0} {1}", string.Join(",", columns.Values.Select(i => dataObject.LogicTableName + "." + i.ColumnName)),
                orderByClause.OrderByType.ToString());
        }

        /// <summary>
        /// 处理查询条件
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="filterClauses"></param>
        /// <param name="CompositeNodeObject"></param>
        /// <param name="dataObject"></param>
        /// <returns></returns>
        public static FilterConditionStatement ParseFiletrClauses(SelectSqlStatement sql, List<FilterClause> filterClauses, 
            CompositeNodeObject CompositeNodeObject, DataObject dataObject)
        {
            var filterConditionStatement = new FilterConditionStatement();
            var filterConditions = new List<ConditionStatement>();
         
            if (filterClauses == null || filterClauses.Count == 0)
                return filterConditionStatement;

            var filterGroups = filterClauses.GroupBy(i => i.FilterField.GroupName);
            if (filterGroups.Count() > 1)
            {
                foreach (var group in filterGroups)
                {
                    var subFilterConditionState = new FilterConditionStatement();
                    foreach (var filter in group)
                    {
                        var conditionStatement = ParseConditionStatement(filter, CompositeNodeObject, dataObject, sql.SqlBuildingInfo.CurrentSqlTable);
                        if (conditionStatement == null) //如果条件不属于当前CompositeNodeObject，跳过
                            continue;
                        subFilterConditionState.ChildCollection.Add(conditionStatement);
                    }

                    filterConditionStatement.ChildCollection.Add(subFilterConditionState);
                }
            }
            else
            {
                foreach (var filter in filterClauses)
                {
                    var conditionStatement = ParseConditionStatement(filter, CompositeNodeObject, dataObject, sql.SqlBuildingInfo.CurrentSqlTable);
                    if (conditionStatement == null) //如果条件不属于当前CompositeNodeObject，跳过
                        continue;
                    filterConditionStatement.ChildCollection.Add(conditionStatement);
                }
            }

            for (int i = 0; i < filterConditionStatement.ChildCollection.Count; i++)
            {
                var conditionStatement = filterConditionStatement.ChildCollection[i] as ConditionStatement;
                conditionStatement.ConditionFieldValue.ConditionFieldName += i;
            }

            return filterConditionStatement;
        }

        /// <summary>
        /// 处理查询条件组
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="filterClauses"></param>
        /// <param name="CompositeNodeObject"></param>
        /// <param name="dataObject"></param>
        /// <returns></returns>
        public static FilterConditionStatement ParseFiletrClauses(QueryGroup queryGroup, CompositeDataObject dm, Dictionary<string, ShardingTarget> routeInfo, Dictionary<string, CompositeNodeObject> map = null)
        {
            var filterConditionStatement = new FilterConditionStatement();

            if (queryGroup == null && queryGroup.FilterClauses == null)
                return null;

            if (map == null)
            {
                map = GetCompositeNodeObjectByElementId(dm);
            }

            if (queryGroup.FilterRelation == SPI.QueryGroupLogicalOperator.AND)
            {
                filterConditionStatement.QueryGroupLogicalOperator = QueryGroupLogicalOperator.AND;
            }
            else if(queryGroup.FilterRelation == SPI.QueryGroupLogicalOperator.OR)
            {
                filterConditionStatement.QueryGroupLogicalOperator = QueryGroupLogicalOperator.OR;
            }
            
            if (queryGroup?.FilterClauses != null)
            {
                foreach (var filter in queryGroup.FilterClauses)
                {
                    CompositeNodeObject CompositeNodeObject;
                    if (filter.FilterField.FieldType == FieldType.Element)
                        CompositeNodeObject = map[filter.FilterField.Field];
                    else
                        CompositeNodeObject = dm.CompositeNodeObjects.First(o => o.ID == filter.FilterField.CompositeNodeObjectID);

                    SqlTable tb = new SqlTable(routeInfo[CompositeNodeObject.DataObject.ID].TableName, routeInfo[CompositeNodeObject.DataObject.ID].TableName, routeInfo[CompositeNodeObject.DataObject.ID].TableName);
                    var conditionStatement = ParseConditionStatement(filter, CompositeNodeObject, CompositeNodeObject.DataObject, tb);
                    if (conditionStatement == null) //如果条件不属于当前CompositeNodeObject，跳过
                        continue;
                    filterConditionStatement.ChildCollection.Add(conditionStatement);
                }
            }

            if (queryGroup?.QueryGroups != null)
            {
                foreach (var group in queryGroup.QueryGroups)
                {
                    var childFilterCondi = new FilterConditionStatement();
                    if (childFilterCondi != null)
                        filterConditionStatement.ChildCollection.Add(ParseFiletrClauses(group, dm, routeInfo));
                }
            }
            return filterConditionStatement;
        }

        /// <summary>
        /// 获取字典key:DomainObejctElement.ID, value: DomainObjet
        /// </summary>
        /// <param name="dmo_ele_id"></param>
        /// <returns></returns>
        private static Dictionary<string, CompositeNodeObject> GetCompositeNodeObjectByElementId(CompositeDataObject dm)
        {
            if (dm?.CompositeNodeObjects == null && dm.CompositeNodeObjects.Count == 0)
            {
                return null;
            }
            Dictionary<string, CompositeNodeObject> dict = new Dictionary<string, CompositeNodeObject>();

            foreach (var dmo in dm.CompositeNodeObjects)
            {
                foreach (var ele in dmo.Elements)
                {
                    dict[ele.ID] = dmo;
                }
            }
            return dict;
        }

        /// <summary>
        /// 统一数据访问服务修改v1.1：添加解析条件节点的方法
        /// </summary>
        /// <param name="sqltable"></param>
        /// <param name="filterClauses"></param>
        /// <param name="CompositeNodeObject"></param>
        /// <param name="dataObject"></param>
        /// <returns></returns>
        public static FilterConditionStatement ParseFiletrClauses(SqlTable sqltable, List<FilterClause> filterClauses,
            CompositeNodeObject CompositeNodeObject, DataObject dataObject)
        {
            var filterConditionStatement = new FilterConditionStatement();
            var filterConditions = new List<ConditionStatement>();

            if (filterClauses == null || filterClauses.Count == 0)
                return filterConditionStatement;

            var filterGroups = filterClauses.GroupBy(i => i.FilterField.GroupName);
            if (filterGroups.Count() > 1)
            {
                foreach (var group in filterGroups)
                {
                    var subFilterConditionState = new FilterConditionStatement();
                    foreach (var filter in group)
                    {
                        var conditionStatement = ParseConditionStatement(filter, CompositeNodeObject, dataObject, sqltable);
                        if (conditionStatement == null) //如果条件不属于当前CompositeNodeObject，跳过
                            continue;
                        subFilterConditionState.ChildCollection.Add(conditionStatement);
                    }

                    filterConditionStatement.ChildCollection.Add(subFilterConditionState);
                }
            }
            else
            {
                foreach (var filter in filterClauses)
                {
                    var conditionStatement = ParseConditionStatement(filter, CompositeNodeObject, dataObject, sqltable);
                    if (conditionStatement == null) //如果条件不属于当前CompositeNodeObject，跳过
                        continue;
                    filterConditionStatement.ChildCollection.Add(conditionStatement);
                }
            }

            ////不需要加后缀
            //for (int i = 0; i < filterConditionStatement.ChildCollection.Count; i++)
            //{
            //    var conditionStatement = filterConditionStatement.ChildCollection[i] as ConditionStatement;
            //    conditionStatement.ConditionFieldValue.ConditionFieldName += i;
            //}

            return filterConditionStatement;
        }

        private static ConditionStatement ParseConditionStatement(FilterClause filterClause, CompositeNodeObject CompositeNodeObject, DataObject dataObject,SqlTable sqlTable)
        {
            //如果条件不属于当前CompositeNodeObject，返回null
            if (!CheckIsValidateCompositeNodeObject(filterClause, CompositeNodeObject))
                return null;
            var conditionStatement = new ConditionStatement();
            conditionStatement.SetLogicalOperator(filterClause.LogicalOperator);
            conditionStatement.ConditionField.Table = sqlTable;
            conditionStatement.RelationalOperator = filterClause.RelationalOperator;
            conditionStatement.ConditionFieldValue = new ConditionFieldValue()
            {
                ValueType = filterClause.FilterFieldValue.ValueType,
                IsNull = filterClause.FilterFieldValue.IsNull,
                Value = filterClause.FilterFieldValue.FiledValue,                
            };

            DataColumn column = null;
            CompositeNodeObjectElement element = null;
            switch (filterClause.FilterField.FieldType)
            {
                case FieldType.Column:  //当前弃用
                    column = dataObject.Columns.FirstOrDefault(i => i.ColumnName.ToLower() == filterClause.FilterField.Field.ToLower());
                    conditionStatement.ConditionField.FieldName = column.ColumnName;                     
                    break;
                case FieldType.Element: //分组、聚合、条件、排序都应该默认指定Element类型,FilterField.Field值为CompositeNodeObjectElement.ID
                    //element = CompositeNodeObject.Elements.FirstOrDefault(i => i.Name == filterClause.FilterField.Field || i.ID == filterClause.FilterField.Field);
                    element = CompositeNodeObject.Elements.FirstOrDefault(ele => string.Equals(ele.ID, filterClause.FilterField.Field));
                    column = dataObject.Columns.FirstOrDefault(i => i.ID == element.DataColumnID);
                    conditionStatement.ConditionField.FieldName = column.ColumnName;
                    break;
                case FieldType.FunctionExpression:
                    conditionStatement.IsExpression = true;
                    conditionStatement.ConditionField.FieldName = filterClause.FilterField.Field;
                    break;
                default:
                    break;
            }
            conditionStatement.ConditionFieldValue.ConditionFieldName = column.ColumnName;
            //筛选条件元素中加入DbType属性，需要根据该属性生成对应的SQL条件语句，例如MSSQL中拼接字符串 + MySQL中为 || 
            conditionStatement.DbType = EnumTools.Convert<DbType>(dataObject.Database.DbType);
            return conditionStatement;
        }

        //public static string ParseFilterClause(FilterClause filterClause, CompositeNodeObject CompositeNodeObject, DataObject dataObject)
        //{
        //    var fieldType = filterClause.FilterField.FieldType;
        //    DataColumn column = null;
        //    CompositeNodeObjectElement element = null;
        //    switch (fieldType)
        //    {
        //        case FieldType.Column:  //当前弃用
        //            column = dataObject.Columns.FirstOrDefault(i => i.ColumnName.ToLower() == filterClause.FilterField.Field.ToLower());
        //            element = CompositeNodeObject.Elements.FirstOrDefault(i => i.DataColumnID == column.ID);
        //            break;
        //        case FieldType.Element:  //分组、聚合、条件、排序都应该默认指定Element类型,FilterField.Field值为CompositeNodeObjectElement.ID
        //            element = CompositeNodeObject.Elements.FirstOrDefault(ele => string.Equals(ele.ID, filterClause.FilterField.Field));
        //            //element = CompositeNodeObject.Elements.FirstOrDefault(i => i.Name == filterClause.FilterField.Field||i.ID == filterClause.FilterField.Field);
        //            column = dataObject.Columns.FirstOrDefault(i => i.ID == element.DataColumnID);
        //            break;
        //        case FieldType.FunctionExpression:
        //            return filterClause.FilterField.Field;
        //    }

        //    var relationOper = RelationalOperatorUtis.ConvertToString(filterClause.RelationalOperator, dataObject.DataSource.DbType);
        //    if (column.IsNumeric)
        //    {
        //        if (filterClause.FilterFieldValue.IsNull)
        //        {
        //            return string.Format("{0} is null ", element.Alias);
        //        }
        //        else
        //        {
        //            if (relationOper.Value)
        //            {
        //                return string.Format("{0} {1} {2}", element.Alias, relationOper.Key,
        //                    filterClause.FilterFieldValue.FiledValue);
        //            }
        //            else
        //            {
        //                return string.Format(element.Alias + " " + relationOper.Key,
        //                   filterClause.FilterFieldValue.FiledValue);
        //            }
        //        }
        //    }
        //    else
        //    {
        //        if (filterClause.FilterFieldValue.IsNull)
        //        {
        //            return string.Format("{0} is null ", element.Alias);
        //        }
        //        else
        //        {
        //            if (relationOper.Value)
        //            {
        //                return string.Format("{0} {1}'{2}'", element.Alias, relationOper.Key,
        //                    filterClause.FilterFieldValue.FiledValue);
        //            }
        //            else
        //            {
        //                return string.Format(element.Alias +" "+ relationOper.Key,
        //                   filterClause.FilterFieldValue.FiledValue);
        //            }
        //        }
        //    }
        //}

        /// <summary>
        /// 判断条件是否属于当前CompositeNodeObject
        /// </summary>
        /// <returns></returns>
        private static bool CheckIsValidateCompositeNodeObject(FilterClause filterClause, CompositeNodeObject CompositeNodeObject)
        {
            if (filterClause.FilterField.FieldType == FieldType.Element)
                return CompositeNodeObject.Elements.Any(ele => string.Equals(ele.ID, filterClause.FilterField.Field));
            else if (filterClause.FilterField.FieldType == FieldType.Column)
                return CompositeNodeObject.ID == filterClause.FilterField.CompositeNodeObjectID;
            else
                return false;
        }
    }
}
